import { useState, useEffect, useCallback } from "react"

// 1. 自定义 Hook: useLocalStorage - 将状态存储在 localStorage 中
function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
  // 初始状态从localStorage获取，如果不存在则使用initialValue
  const [storedValue, setStoredValue] = useState<T>(() => {
    try {
      const item = window.localStorage.getItem(key)
      return item ? JSON.parse(item) : initialValue
    } catch (error) {
      console.error(`Error reading localStorage key "${key}":`, error)
      return initialValue
    }
  })

  // 封装setState，在更新state的同时更新localStorage
  const setValue = useCallback(
    (value: T | ((val: T) => T)) => {
      try {
        // 允许value是一个函数，类似于useState
        const valueToStore =
          value instanceof Function ? value(storedValue) : value

        // 更新状态
        setStoredValue(valueToStore)

        // 更新localStorage
        window.localStorage.setItem(key, JSON.stringify(valueToStore))
      } catch (error) {
        console.error(`Error setting localStorage key "${key}":`, error)
      }
    },
    [key, storedValue]
  )

  return [storedValue, setValue]
}

// 2. 自定义 Hook: useWindowSize - 追踪窗口尺寸
function useWindowSize() {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  })

  useEffect(() => {
    // 处理窗口大小变化的事件处理函数
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      })
    }

    // 添加事件监听
    window.addEventListener("resize", handleResize)

    // 清理函数
    return () => window.removeEventListener("resize", handleResize)
  }, [])

  return windowSize
}

// 3. 自定义 Hook: useFetch - 简化数据获取
function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    const controller = new AbortController()
    const signal = controller.signal

    async function fetchData() {
      try {
        setLoading(true)
        const response = await fetch(url, { signal })
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`)
        }
        const result = await response.json()
        if (!signal.aborted) {
          setData(result)
          setError(null)
        }
      } catch (err) {
        if (!signal.aborted && err instanceof Error) {
          setError(err.message)
          setData(null)
        }
      } finally {
        if (!signal.aborted) {
          setLoading(false)
        }
      }
    }

    fetchData()

    return () => {
      controller.abort()
    }
  }, [url])

  return { data, loading, error }
}

// 4. 自定义 Hook: useDebounce - 减少函数调用频率
function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value)

  useEffect(() => {
    // 设置一个延时，delay毫秒后更新debouncedValue
    const timer = setTimeout(() => {
      setDebouncedValue(value)
    }, delay)

    // 清理函数 - 如果在delay时间内value再次改变，取消之前的timeout
    return () => {
      clearTimeout(timer)
    }
  }, [value, delay])

  return debouncedValue
}

interface Post {
  id: number
  title: string
  body: string
}

// 主组件
const CustomHookDemo = () => {
  // 使用自定义Hook: useLocalStorage
  const [name, setName] = useLocalStorage<string>("name", "")

  // 使用自定义Hook: useWindowSize
  const windowSize = useWindowSize()

  // 使用自定义Hook: useDebounce
  const [searchTerm, setSearchTerm] = useState("")
  const debouncedSearchTerm = useDebounce(searchTerm, 500)

  // 使用自定义Hook: useFetch
  const { data, loading, error } = useFetch<Post[]>(
    "https://jsonplaceholder.typicode.com/posts?_limit=5"
  )

  return (
    <div className="demo-section">
      <h2>自定义 Hooks 面试题讲解</h2>

      <div className="card">
        <h3>1. 什么是自定义 Hook?</h3>
        <p>
          自定义 Hook 是一种复用 React 状态逻辑的函数，它利用 React 内置的 Hook
          来提供特定的功能。 自定义 Hook 的名称必须以 "use"
          开头，这是一个约定，确保 React 能够检查 Hook 规则的遵守情况。
        </p>

        <div className="code-example">
          <code>
            // 基本结构
            {/* function useCustomHook(arg1, arg2) {"{"}
              // 可以使用任何内置的 React Hook
              const [state, setState] = useState(initialState);
              useEffect(() => {})
                // 一些副作用逻辑
              {"}"}, [dependencies]);
              
              // 自定义逻辑...
              
              // 返回自定义状态或函数
              return {"{"}state, customFunction{"}"};
            {"}"} */}
          </code>
        </div>
      </div>

      <div className="card">
        <h3>2. 常见面试问题: 自定义 Hook 解决了什么问题?</h3>
        <p>自定义 Hook 主要解决了以下问题:</p>
        <ul>
          <li>提高代码复用性，避免重复逻辑</li>
          <li>将复杂的状态逻辑从组件中抽离，使组件更简洁和专注</li>
          <li>更好地组织和分离关注点</li>
          <li>使第三方库的集成更简单</li>
          <li>使测试更容易进行</li>
        </ul>
      </div>

      <div className="card">
        <h3>3. 实例: useLocalStorage</h3>
        <p>
          这个自定义 Hook 允许我们在 localStorage 中持久化存储状态，用法类似
          useState。
        </p>

        <div className="example-output">
          <p>输入名称 (会保存到 localStorage):</p>
          <input
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="输入你的名字..."
            style={{ padding: "8px", width: "100%", marginBottom: "10px" }}
          />
          <p>
            当前保存的名称: <strong>{name || "(空)"}</strong>
          </p>
          <p>
            <small>提示: 刷新页面后，输入的名称会保留</small>
          </p>

          <div className="code-example">
            {/* <code>
              // 使用方式
              const [name, setName] = useLocalStorage("name", "");
              
              // Hook 实现
              function useLocalStorage(key, initialValue) {"{"}
                const [storedValue, setStoredValue] = useState(() => {"{"}
                  const item = localStorage.getItem(key);
                  return item ? JSON.parse(item) : initialValue;
                {"}"});
                
                const setValue = value => {"{"}
                  setStoredValue(value);
                  localStorage.setItem(key, JSON.stringify(value));
                {"}"};
                
                return [storedValue, setValue];
              {"}"}
            </code> */}
          </div>
        </div>
      </div>

      <div className="card">
        <h3>4. 实例: useWindowSize</h3>
        <p>
          该 Hook
          追踪浏览器窗口的尺寸变化。当窗口大小改变时，组件会自动获得最新的窗口尺寸。
        </p>

        <div className="example-output">
          <p>当前窗口尺寸:</p>
          <div
            style={{
              border: "1px solid #ddd",
              padding: "15px",
              borderRadius: "5px",
              textAlign: "center",
              marginBottom: "10px",
            }}
          >
            宽度: <strong>{windowSize.width}px</strong> | 高度:{" "}
            <strong>{windowSize.height}px</strong>
          </div>
          <p>
            <small>提示: 调整浏览器窗口大小查看变化</small>
          </p>
        </div>
      </div>

      <div className="card">
        <h3>5. 实例: useDebounce</h3>
        <p>
          这个 Hook 用于延迟处理快速变化的值，比如搜索输入，可以减少不必要的 API
          调用或重渲染。
        </p>

        <div className="example-output">
          <p>搜索输入 (带防抖):</p>
          <input
            type="text"
            value={searchTerm}
            onChange={(e) => setSearchTerm(e.target.value)}
            placeholder="输入搜索词..."
            style={{ padding: "8px", width: "100%", marginBottom: "10px" }}
          />
          <p>
            实时输入值: <strong>{searchTerm}</strong>
          </p>
          <p>
            防抖后的值: <strong>{debouncedSearchTerm}</strong>
          </p>
          <p>
            <small>提示: 快速输入时，防抖值会延迟更新</small>
          </p>

          <div className="code-example">
            <code>
              {/* function useDebounce(value, delay) {
                const [debouncedValue, setDebouncedValue] = useState(value);
                
                useEffect(() => {
                  const timer = setTimeout(() => setDebouncedValue(value), delay);
                  return () => clearTimeout(timer);
                }, [value, delay]);
                
                return debouncedValue;
              } */}
            </code>
          </div>
        </div>
      </div>

      <div className="card">
        <h3>6. 实例: useFetch</h3>
        <p>这个 Hook 简化了数据获取逻辑，包括加载状态和错误处理。</p>

        <div className="example-output">
          <h4>从 JSONPlaceholder API 获取的帖子:</h4>

          {loading && <p>加载中...</p>}

          {error && (
            <div
              style={{
                color: "red",
                padding: "10px",
                backgroundColor: "#ffeeee",
                borderRadius: "5px",
              }}
            >
              加载错误: {error}
            </div>
          )}

          {data && (
            <ul style={{ listStyle: "none", padding: 0 }}>
              {data.map((post) => (
                <li
                  key={post.id}
                  style={{
                    marginBottom: "10px",
                    padding: "10px",
                    border: "1px solid #eee",
                    borderRadius: "5px",
                  }}
                >
                  <h4 style={{ margin: "0 0 5px 0" }}>{post.title}</h4>
                  <p style={{ margin: 0, color: "#666" }}>
                    {post.body.substring(0, 100)}...
                  </p>
                </li>
              ))}
            </ul>
          )}
        </div>
      </div>

      <div className="card">
        <h3>7. 常见面试问题: 自定义 Hook 的规则</h3>
        <ul>
          <li>Hook 名称必须以 "use" 开头，这是一个约定</li>
          <li>自定义 Hook 内部可以调用其他 Hook（内置或自定义）</li>
          <li>
            与内置 Hook 相同，必须遵守 Hook 的规则：
            <ul>
              <li>只在 React 函数组件或自定义 Hook 中调用</li>
              <li>只在组件的顶层调用，不在循环、条件或嵌套函数中调用</li>
            </ul>
          </li>
          <li>
            自定义 Hook 之间可以传递信息，但每个组件调用同一个 Hook
            会获得独立的状态
          </li>
        </ul>
      </div>

      <div className="card">
        <h3>8. 自定义 Hook 面试核心要点</h3>
        <ul>
          <li>
            自定义 Hook 是一种提取和共享组件状态逻辑的机制，而不是共享状态本身
          </li>
          <li>每个使用自定义 Hook 的组件都有自己独立的状态</li>
          <li>
            自定义 Hook 让我们能够在不修改组件层次结构的情况下复用状态逻辑
          </li>
          <li>
            自定义 Hook 是 React Hooks 模型的一个强大特性，它展示了 Hooks
            的灵活性和可组合性
          </li>
          <li>常见的自定义 Hook 用例包括表单处理、动画、订阅声明、计时器等</li>
          <li>
            从第三方库中导入自定义 Hook 是安全的，因为 Hook
            遵循与普通函数导入相同的规则
          </li>
          <li>
            测试自定义 Hook 可以通过创建一个使用该 Hook
            的测试组件，或使用专门的测试库（如 @testing-library/react-hooks）
          </li>
        </ul>
      </div>
    </div>
  )
}

export default CustomHookDemo
